/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import dk.lindhardt.gwt.geie.shared.*;

import java.util.*;

/**
 * User: AnAmuser
 * Date: 29-05-11
 * <p/>
 * Class for building a {@link TableLayout} from a {@link com.google.gwt.user.client.ui.HTMLTable}
 */
@SuppressWarnings({"JavaDoc"})
public class HTMLTable2TableLayout {

   private HTMLTable htmlTable;
   private List<DateTimeFormat> dateFormats;
   private List<NumberFormat> numberFormats;
   private TableLayout tableLayout;
   private Map<String, CellFormat> cellFormatMap;

   /**
    * Creates new instance
    * @param htmlTable the table to build the {@link dk.lindhardt.gwt.geie.shared.TableLayout} upon
    */
   public HTMLTable2TableLayout(HTMLTable htmlTable) {
      this.htmlTable = htmlTable;
      this.tableLayout = new TableLayout();
      dateFormats = new ArrayList<DateTimeFormat>();
      numberFormats = new ArrayList<NumberFormat>();
      cellFormatMap = new HashMap<String, CellFormat>();
      cellFormatMap.put("default", new CellFormat());
   }

   /**
    * Builds the {@link TableLayout}
    * @return the {@link dk.lindhardt.gwt.geie.shared.TableLayout}
    */
   public TableLayout build() {
      ColumnCorrectionBookKeeper correctionBookKeeper = tableLayout.getColumnCorrectionBookKeeper();
      for (int r = 0; r < htmlTable.getRowCount(); r++) {
         for (int c = 0; c < htmlTable.getCellCount(r); c++) {
            Element cellElement = htmlTable.getCellFormatter().getElement(r, c);

            Position position = new Position(r, c);
            int columnCorrection = correctionBookKeeper.getColumnCorrection(position, PositioningType.HTML);
            position.setColumn(c + columnCorrection);
            int columnSpan = getColumnSpan(cellElement);
            int rowSpan = getRowSpan(cellElement);
            position.setSpanColumn(columnSpan);
            position.setSpanRow(rowSpan);

            Widget cellWidget = htmlTable.getWidget(r, c);
            if (cellWidget != null) {
               Cell cell = createCell(cellElement, cellWidget, position);
               if (cell != null) {
                  tableLayout.addCell(cell);
               }
            }

            Element element = htmlTable.getColumnFormatter().getElement(c);
            String width = element.getAttribute("width");
            if (width != null && width.length() > 0) {
               tableLayout.setColumnWidth(c, getPxSize(width));
            }
         }
      }

      setFormatsOnTableLayout();

      return tableLayout;
   }

   /**
    * Adds a date format to this parser
    * @param dateFormat the date format to be added
    */
   public void addDateFormat(DateTimeFormat dateFormat) {
      dateFormats.add(dateFormat);
   }

   /**
    * Adds a number format to this parser
    * @param numberFormat the number format to be added
    */
   public void addNumberFormat(NumberFormat numberFormat) {
      numberFormats.add(numberFormat);
   }

   /**
    * Moves the created formats to the table layout
    */
   private void setFormatsOnTableLayout() {
      for (String key : cellFormatMap.keySet()) {
         tableLayout.addCellFormat(key, cellFormatMap.get(key));
      }
   }

   private int getRowSpan(Element cellElement) {
      String rowspan = DOM.getElementAttribute(cellElement, "rowspan");
      if (rowspan != null && rowspan.length() > 0) {
         try {
            return Integer.parseInt(rowspan);
         } catch (Exception e) {
            GWT.log("Could not parse rowspan to int. It was " + rowspan);
         }
      }
      return 0;
   }

   private int getColumnSpan(Element cellElement) {
      String colspan = DOM.getElementAttribute(cellElement, "colspan");
      if (colspan != null && colspan.length() > 0) {
         try {
            return Integer.parseInt(colspan);
         } catch (Exception e) {
            GWT.log("Could not parse colspan to int. It was " + colspan);
         }
      }
      return 0;
   }

   /**
    * Creates a cell from the widget in the cell
    */
   private Cell createCell(Element cellElement, Widget cellWidget, Position position) {
      if (cellWidget instanceof GeieAnchor) {
         GeieAnchor geieAnchor = (GeieAnchor) cellWidget;
         Cell cell = createCell(cellElement, geieAnchor.getChild(), position);
         cell.setLink(new Link(geieAnchor.getHref()));
         return cell;
      } else {
         if (cellWidget instanceof Label) {
            Label label = (Label) cellWidget;
            String text = label.getText();

            Date date = null;
            for (DateTimeFormat dateFormat : dateFormats) {
               try {
                  date = dateFormat.parse(text);
                  if (date != null) {
                     return new DateCell(
                           date,
                           position,
                           prepareCellStyle(cellElement, dateFormat.getPattern()));
                  }
               } catch (IllegalArgumentException ignored) {
               }
            }

            Double number = null;
            for (NumberFormat numberFormat : numberFormats) {
               try {
                  number = numberFormat.parse(text);
                  if (number != null) {
                     return new NumberCell(
                           number,
                           position,
                           prepareCellStyle(cellElement, numberFormat.getPattern()));
                  }
               } catch (NumberFormatException ignored) {
               }
            }

            // default
            return new LabelCell(text, position, prepareCellStyle(cellElement, null));
         }

         GWT.log("Widget in cell " + position.toString() + " is not a supported type and is therefore not moved to TableLayout");
         return null;
      }
   }

   /**
    * Prepares a cell format and returns it's key
    */
   private CellFormatKey prepareCellStyle(Element cellElement, String pattern) {
      CellFormat cellFormat = new CellFormat();
      cellFormat.setPattern(pattern);

      // Get cell properties which is not style
      String hAlign = DOM.getElementAttribute(cellElement, "align");
      String vAlign = DOM.getElementAttribute(cellElement, "valign");
      CellFormat.Alignment alignment = getHorizontalAlign(hAlign);
      if (alignment != null) {
         cellFormat.setAlignment(alignment);
      }
      CellFormat.VAlignment vAlignment = getVAlign(vAlign);
      if (vAlignment != null) {
         cellFormat.setValignment(vAlignment);
      }

      ElementStyleExtractor styleExtractor = new ElementStyleExtractor(cellElement);
      styleExtractor.mergeStyles(cellFormat);

      tableLayout.addCellFormat(cellFormat.toString(), cellFormat);
      return new CellFormatKey(cellFormat.toString());
   }

   /**
    * Sets vertical alignment
    */
   private CellFormat.VAlignment getVAlign(String valign) {
      if (valign != null && valign.length() > 0) {
         String string = valign.toLowerCase();
         if (string.equals("top")) {
            return CellFormat.VAlignment.TOP;
         } else if (string.equals("middle")) {
            return CellFormat.VAlignment.CENTER;
         } else if (string.equals("bottom")) {
            return CellFormat.VAlignment.BOTTOM;
         }
      }
      return null;
   }

   /**
    * Sets text alignment property
    */
   private CellFormat.Alignment getHorizontalAlign(String align) {
      if (align != null && align.length() > 0) {
         if (align.contains("left")) {
            return CellFormat.Alignment.LEFT;
         } else if (align.contains("right")) {
            return CellFormat.Alignment.RIGHT;
         } else if (align.contains("center")) {
            return CellFormat.Alignment.CENTER;
         }
      }
      return null;
   }


   /**
    * Gets size in px´s from a string
    */
   private Integer getPxSize(String size) {
      if (size.endsWith("%")) {
         int percent = Integer.parseInt(size.substring(0, size.length() - 1));
         return Math.round((percent / 100.0f) * 16f);
      } else if (size.endsWith("px")) {
         String px = size.substring(0, size.length() - 2);
         return Integer.parseInt(px);
      } else if (size.endsWith("em")) {
         String px = size.substring(0, size.length() - 2);
         return Math.round(Float.parseFloat(px) * 16f);
      } else if (size.matches("\\d+")) {
         return Integer.parseInt(size);
      }
      return null;
   }

}
